package net.aiotos.sdk;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import Glacier2.SessionNotExistException;
import IceUtilInternal.StringUtil;
import net.aiotos.common.dto.*;
import net.aiotos.common.exception.SelfOfflineError;
import net.aiotos.common.ice.IceService;
import net.aiotos.common.ice.routellib.CallbackReceiverDisp;
import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import Ice.Current;
import sun.reflect.generics.reflectiveObjects.NotImplementedException;

/**
 * @author zws
 * @date 2021.05.25
 */
@Slf4j
public class IOTOSysI {
    /**
     * 当前运行目录
     */
    public final String runDir;
    /**
     * 当前通信网关ID
     */
    @Setter
    @Getter
    protected String uuid;
    /**
     * 当前网关点表
     */
    @Setter
    @Getter
    protected IonodeDTO ionodeDTO;

    /**
     * 当前会话SESSION
     */
    @Getter
    protected String uuidSession;
    /**
     * 当前网关登录参数
     */
    @Setter
    @Getter
    protected LoginDTO loginDTO;
    public final IceService iceService;
    private LoginResp loginResp;

    /**
     * 所有带有@Getter注解的属性，只要是非public都要getXXX()访问，设置setXXX(value)设置数据
     */


    @Getter
    protected CallbackReceiverDisp callback = new CallbackReceiverDisp() {
        @Override
        public String callback(String fromUuid, String data, Current __current) {
            throw new NotImplementedException();
        }
    };

    /**
     * @param runDir 当前运行目录，用于获取证书目录
     */
    public IOTOSysI(@NonNull String runDir) {
        this(runDir, new IceService());
    }

    private IOTOSysI(@NonNull String runDir, @NonNull IceService iceService) {
        this.runDir = runDir;
        this.iceService = iceService;
    }

    /**
     * 登录接口，需要使用点对点通信
     * @param loginDTO 登录参数
     * @param callBackReceiver 回调处理接口
     * @return LoginResp 登录结果
     * @throws Exception
     */
    public LoginResp login(@NonNull final LoginDTO loginDTO, @NonNull final CallbackReceiverDisp callBackReceiver) throws Exception {
        this.callback = callBackReceiver;
        return login(loginDTO);
    }

    /**
     * 登录接口,无需使用点对点通信。
     * @param loginDTO 登录参数
     * @return LoginResp 处理结果
     * @throws Exception
     */
    public LoginResp login(@NonNull LoginDTO loginDTO) throws Exception {
        this.loginDTO = loginDTO;
        loginResp = iceService.login(loginDTO, this.callback);
        loginResp.check();
        this.uuid = loginDTO.uuid;
        this.uuidSession = loginResp.getUuidSession();

        //sdk处理
        //获取平台传过来的表信息
        ResultDTO msg = iceService.getTableDetail();
        msg.check();
        ionodeDTO = msg.toJavaObject(IonodeDTO.class);
        return loginResp;
    }

    /**
     * 接口登出
     * @return ResultDTO
     */
    public ResultDTO logout() {
        ResultDTO resultDTO = iceService.logout();
        return resultDTO;
    }


    /**
     * 接口点对点通信(向指定用户发送消息)
     *
     * @param toUuid 对方通信网关UUID
     * @param data 数据格式，双方协商
     * @return String Json String
     */
    public String sendMsg(@NonNull String toUuid, @NonNull String data) {
        String retJson = iceService.sendMsg(toUuid, data);
        return retJson;
    }

    /**
     * 接口发布订阅
     *
     * @param idList 订阅网关列表
     * @return ResultDTO
     */
    public ResultDTO subMsg(@NonNull String[] idList) {
        ResultDTO result = null;
         for (String pointId : idList) {
            String point = getPoint(pointId, "");
            result = iceService.subMsg(point);
        }
        return result;
    }

    /**
     * 批量发布
     *
     * @param values
     * @return
     */
    @Deprecated
    public List<ResultDTO> PubMsgs(@NonNull List<DataPubDTO> values) throws Exception {
        List<ResultDTO> listRet = new ArrayList<>();
        for (DataPubDTO dataValue : values) {
            log.info("{}=>{}", dataValue.getId(), dataValue.getValue());
            String point = getPoint(dataValue.getId(), dataValue.getValue().toString());
            ResultDTO resultDTO = iceService.syncPubMsg(point);
            listRet.add(resultDTO);
        }
        return listRet;
    }

    /**
     * 发布数据
     *
     * @param idifno 三段式ID(网关ID.设备ID。数据点ID)
     * @param value 发布的数据值
     * @return ResultDTO
     */
    @Deprecated
    public ResultDTO PubMsg(@NonNull String idifno, @NonNull String value) throws Exception {
        ResultDTO resultDTO = null;
        String point = getPoint(idifno, value);
        resultDTO = iceService.syncPubMsg(point);
        if(resultDTO.getCode() == ErrorEnum.SelfOffline) {
            log.warn(resultDTO.toString());
            throw new SelfOfflineError();
        }
        return resultDTO;
    }

    /**
     * 数据发布
     * @param data DataPubDTO
     * @return ResultDTO
     * @throws Exception
     */
    public ResultDTO pubMsg(@NonNull DataPubDTO data) throws Exception {
        ResultDTO resultDTO = null;
        String point = getPoint(data.getId(), data.getValue().toString());
        resultDTO = iceService.syncPubMsg(point);
        if(resultDTO.getCode() == ErrorEnum.SelfOffline) {
            log.warn(resultDTO.toString());
            throw new SelfOfflineError();
        }
        return resultDTO;
    }

    /**
     * 获取监测点在平台数据中间件的值
     *
     * @param point  三段式ID(网关ID.设备ID。数据点ID)
     * @return ResultDTO
     */
    public ResultDTO getPlatformData(@NonNull String point) {
        try {
            String deviceOid = point.split("\\.")[1];
            String dataOid = point.split("\\.")[2];
            String p = getPoint(point, "");
            IonodeDTO ionodeDTO = iceService.getPlatformData(p);
            DataDTO dataDTO = ionodeDTO.getProperties().get(deviceOid).getData().get(dataOid);
            return ResultDTO.code(0).msg("OK").data(dataDTO);
        } catch (Exception e) {
            return ResultDTO.code(514).msg("Argument error");
        }
    }

    /**
     * 获取监测点在平台数据中间件的值
     *
     * @param point
     * @param param
     * @return
     */
    public ResultDTO getPlatformData(@NonNull String point, @NonNull String param) {
        String p = getPoint(point, param);
        IonodeDTO ionodeDTO = iceService.getPlatformData(p);

        String[] strings = StringUtil.splitString(point, ".");
        String deviceOid = strings[1];
        String dataOid = strings[2];

        DeviceDTO deviceDTO = ionodeDTO.getProperties().get(deviceOid);
        DataDTO dataDTO = deviceDTO.getData().get(dataOid);

        return ResultDTO.ret(0, "OK", dataDTO);
    }


    /**
     * 获取监测点设备的值
     *
     * @param point
     * @return
     */
    public ResultDTO getDeviceData(@NonNull String point) {
        try {
            String deviceOid = point.split("\\.")[1];
            String dataOid = point.split("\\.")[2];
            point = getPoint(point, "");
            ResultDTO resultDTO = iceService.getDeviceData(point);
            if (resultDTO.getCode() == ErrorEnum.Successfull && resultDTO.getData() != null) {
                IonodeDTO ionodeDTO = resultDTO.toJavaObject(IonodeDTO.class);
                DataDTO dataDTO = ionodeDTO.getBody().getProperties().get(deviceOid).getData().get(dataOid);
                return ret(dataDTO, resultDTO.getCode().code, resultDTO.getMsg());
            } else {
                return resultDTO;
            }
        } catch (Exception e) {
            return ret(null, 514, "Argument error");
        }
    }


    /**
     * 获取监测点设备的值
     *
     * @param point
     * @param param
     * @return
     */
    public ResultDTO getDeviceData(@NonNull String point, @NonNull String param) {
        try {
            String deviceOid = point.split("\\.")[1];
            String dataOid = point.split("\\.")[2];
            point = getPoint(point, param);
            ResultDTO resultDTO = iceService.getDeviceData(point);
            if (resultDTO.getCode() == ErrorEnum.Successfull && resultDTO.getData() != null) {
                IonodeDTO ionodeDTO = resultDTO.toJavaObject(IonodeDTO.class);
                if(ionodeDTO.getBody() != null) {
                    ionodeDTO = ionodeDTO.getBody();
                    return ret(ionodeDTO.getProperties().get(deviceOid).getData().get(dataOid), resultDTO.getCode().code, resultDTO.getMsg());
                }
            }
            return resultDTO;
        } catch (Exception e) {
            return ret( 514, "Argument error");
        }
    }

    /**
     * 组装点表数据
     *
     * @param point
     * @param value
     * @return
     */
    private String getPoint(@NonNull String point, @NonNull String value) {
        String[] points = point.split("\\.");
        if(points.length != 3) {
            throw new IllegalArgumentException(point + "参数格式有错误，参数必须为三段式(网关ID.设备ID.数据点ID)");
        }
        long getTimeStamp = new Date().getTime(); //当前时间戳
        String res = String.format("[{ \"id\": \"%s\",\"properties\": { \"%s\": { \"data\": { \"%s\": { \"value\":\"%s\" ,\"timestamp\": %s } } } }}]", points[0], points[1], points[2], value, getTimeStamp);
        return res;
    }

    /**
     * 组装返回信息
     * @param data
     * @param code
     * @param description
     * @return
     */
//    protected String ret(Object data, int code, String description) {
//        Map<String, Object> map = new HashMap<String, Object>();
//
//        if (code > 1000) {
//            code = 1;
//            description = "Code need greater than 1000";
//            data = null;
//        }
//        map.put("code", code);
//        map.put("msg", description);
//        map.put("data", data);
//        return JSON.toJSONString(map, SerializerFeature.WriteMapNullValue);
//    }

    public ResultDTO ret(Object data, int code, String description) {

        if (code > 1000) {
            code = 1;
            description = "Code need greater than 1000";
            data = null;
        }
        return ResultDTO.ret(code, description, data);
    }

    public ResultDTO ret(int code, String description) {
        Object data = null;
        if (code > 1000) {
            code = 1;
            description = "Code need greater than 1000";

        }
        return ResultDTO.ret(code, description, data);
    }

    @SuppressWarnings("unused")
    protected String _ret(String info) {
        Map<String, Object> map = new HashMap<String, Object>();
        String msg;
        int code;
        Object data;
        try {
            JSONObject json = JSON.parseObject(info);
            msg = json.getString("msg");
            code = json.getIntValue("code");
            data = json.get("data");
        } catch (Exception e) {
            code = 517;
            msg = "Format return error. eg:{'code':'1001','msg':'msg info','data':data}";
            data = null;
        }
        if (code > 1000) {
            code = 517;
            msg = "Code need greater than 1000";
            data = null;
        } else {
            code = 0;
            msg = "OK";
            data = null;
        }
        map.put("code", code);
        map.put("msg", msg);
        map.put("data", data);
        return JSON.toJSONString(map, SerializerFeature.WriteMapNullValue);
    }

    /**
     * 设备上线上报
     *
     * @param devId String “设备ID”
     */
    public ResultDTO devOnline(@NonNull String devId) throws Exception {
        ResultDTO resultDTO = iceService.devOnline(devId);
        resultDTO.check();
        return resultDTO;
    }

    /**
     * 设备下线上报
     *
     * @param devId String “设备ID”
     * @return ResultDTO
     */
    public ResultDTO devOffline(@NonNull String devId) throws Exception {
        ResultDTO resultDTO = iceService.devOffline(devId);
        resultDTO.check();
        return resultDTO;
    }

    /**
     * 获取点表详情
     * @return ResultDTO
     */
    public ResultDTO getTableDetail() throws Exception {
        ResultDTO resultDTO = iceService.getTableDetail(uuidSession);
        resultDTO.check();
        return resultDTO;
    }

    /**
     * 向服务器发送心跳
     * @return HeartbeatDTO
     */
    public HeartbeatDTO heartbeat() throws Exception {
        HeartbeatDTO heartbeatDTO = iceService.heartbeat();
        heartbeatDTO.check();
        return heartbeatDTO;
    }

    /**
     * 获取服务器心跳周期
     * @return int
     */
    public int getHeartbeat() {
        return loginResp.getHeartbeat();
    }

    /**
     * 获取授权SESSION
     * @return LoginResp
     * @throws Exception
     */
    public LoginResp loginWeb() throws Exception {
        return iceService.loginWeb(loginDTO);
    }

    /**
     * 刷新通信会话
     * @return void
     */
    public void routerPrxRefreshSession() throws SessionNotExistException {
        iceService.getRouterPrx().refreshSession();
    }

    public ResultDTO updateTable(@NonNull IonodeDTO ionodeDTO) {
        return iceService.updateTable(ionodeDTO);
    }

    public void destroy() throws SessionNotExistException {
        iceService.destroy();
    }

}
	
